package net.sudot.commons.security;

import net.sudot.chess.business.model.User;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.DisabledAccountException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.SimpleAccount;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.PrincipalCollection;

import java.util.Set;

/**
 * 授权域
 *
 * @author tangjialin on 2017-06-14 0014.
 */
public class AuthorizingRealm extends org.apache.shiro.realm.AuthorizingRealm {

    protected AuthenticationProvider authenticationProvider;

    public AuthorizingRealm(AuthenticationProvider authenticationProvider) {
        super();
        this.authenticationProvider = authenticationProvider;
    }

    public AuthorizingRealm(CacheManager cacheManager, AuthenticationProvider authenticationProvider) {
        super(cacheManager);
        this.authenticationProvider = authenticationProvider;
    }

    public AuthorizingRealm(CredentialsMatcher matcher, AuthenticationProvider authenticationProvider) {
        super(matcher);
        this.authenticationProvider = authenticationProvider;
    }

    public AuthorizingRealm(CacheManager cacheManager, CredentialsMatcher matcher, AuthenticationProvider authenticationProvider) {
        super(cacheManager, matcher);
        this.authenticationProvider = authenticationProvider;
    }

    /**
     * 执行获取认证信息的操作(即:进行身份认证的操作)
     *
     * @param authenticationToken 令牌
     * @return 认证信息
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        User user = authenticationProvider.getUser(authenticationToken.getPrincipal());
        if (user == null) { throw new UnknownAccountException(); }
        if (!Boolean.TRUE.equals(user.getEnabled())) { throw new DisabledAccountException(); }
        if (Boolean.TRUE.equals(user.getLocked())) { throw new LockedAccountException(); }
        SimpleAccount account = new SimpleAccount(user, user.getPassword(), getName());
        return account;
    }

    /**
     * 执行获取授权信息的操作(即:获取用户权限信息的操作)
     *
     * @param principalCollection 身份集合
     * @return 授权信息
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        Session session = SecurityUtils.getSubject().getSession();
        SimpleAuthorizationInfo authorizationInfo = (SimpleAuthorizationInfo) session.getAttribute(SimpleAuthorizationInfo.class.getName());
        if (authorizationInfo != null) { return authorizationInfo; }
        authorizationInfo = new SimpleAuthorizationInfo();
        for (Object principal : principalCollection.fromRealm(getName())) {
            if (!(principal instanceof User)) { continue; }
            User user = (User) principal;
            Set<String> permissions = authenticationProvider.getPermissions(user);
            if (permissions != null) {
                authorizationInfo.addStringPermissions(permissions);
            }
            Set<String> roles = authenticationProvider.getRoles(user);
            if (roles != null) {
                authorizationInfo.addRoles(roles);
            }
        }
        session.setAttribute(SimpleAuthorizationInfo.class.getName(), authorizationInfo);
        return authorizationInfo;
    }

    public AuthenticationProvider getAuthenticationProvider() {
        return authenticationProvider;
    }

    public AuthorizingRealm setAuthenticationProvider(AuthenticationProvider authenticationProvider) {
        this.authenticationProvider = authenticationProvider;
        return this;
    }
}